<!DOCTYPE html>
<html lang=zh-CN>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta property="og:description" content="samuel-24276的博客">
    <meta property="og:type" content="website">
    <meta name="description" content="samuel-24276的博客">
    <meta name="keyword"  content="aircloud">
    <link rel="shortcut icon" href="/myblog/img/favicon.ico">

    <title>
        
        qmetatype-knows-your-types - samuel-24276的博客 | samuel-24276&#39;s Blog
        
    </title>

    <!-- Custom CSS -->
    
<link rel="stylesheet" href="/myblog/css/aircloud.css">

    
<link rel="stylesheet" href="/myblog/css/gitment.css">

    <!--<link rel="stylesheet" href="https://imsun.github.io/gitment/style/default.css">-->
    <link href="//at.alicdn.com/t/font_620856_28hi1hpxx24.css" rel="stylesheet" type="text/css">
    <!-- ga & ba script hoook -->
    <script></script>
<meta name="generator" content="Hexo 7.1.1"></head>

<body>

<div class="site-nav-toggle" id="site-nav-toggle">
    <button>
        <span class="btn-bar"></span>
        <span class="btn-bar"></span>
        <span class="btn-bar"></span>
    </button>
</div>

<div class="index-about">
    <i> Like what you do, do what you like </i>
</div>

<div class="index-container">
    
    <div class="index-left">
        
<div class="nav" id="nav">
    <div class="avatar-name">
        <div class="avatar ">
            <img src="/myblog/img/avatar.jpg" />
        </div>
        <div class="name">
            <i>samuel-24276</i>
        </div>
    </div>
    <div class="contents" id="nav-content">
        <ul>
            <li >
                <a href="/myblog/">
                    <i class="iconfont icon-shouye1"></i>
                    <span>主页</span>
                </a>
            </li>
            <li >
                <a href="/myblog/tags">
                    <i class="iconfont icon-biaoqian1"></i>
                    <span>标签</span>
                </a>
            </li>
            <li >
                <a href="/myblog/archives">
                    <i class="iconfont icon-guidang2"></i>
                    <span>存档</span>
                </a>
            </li>
            <li >
                <a href="/myblog/collect/">
                    <i class="iconfont icon-shoucang1"></i>
                    <span>收藏</span>
                </a>
            </li>
            <li >
                <a href="/myblog/about/">
                    <i class="iconfont icon-guanyu2"></i>
                    <span>关于</span>
                </a>
            </li>
            
            <li>
                <a id="search">
                    <i class="iconfont icon-sousuo1"></i>
                    <span>搜索</span>
                </a>
            </li>
            
        </ul>
    </div>
    
        <div id="toc" class="toc-article">
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Why-does-Qt-need-runtime-dynamic-type-information"><span class="toc-text">Why does Qt need runtime dynamic type information?</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#What-information-does-QMetaType-keep"><span class="toc-text">What information does QMetaType keep?</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#QTypeInfo"><span class="toc-text">QTypeInfo</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#How-does-it-work"><span class="toc-text">How does it work?</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#The-Q-DECLARE-METATYPE-macro"><span class="toc-text">The Q_DECLARE_METATYPE macro.</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#qRegisterMetaType"><span class="toc-text">qRegisterMetaType</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Automatic-registration"><span class="toc-text">Automatic registration.</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Research"><span class="toc-text">Research</span></a></li></ol>
</div>
    
</div>


<div class="search-field" id="search-field">
    <div class="search-bg" id="search-bg"></div>
    <div class="search-container">
        <div class="search-input">
            <span id="esc-search"> <i class="icon-fanhui iconfont"></i></span>
            <input id="search-input"/>
            <span id="begin-search">搜索</span>
        </div>
        <div class="search-result-container" id="search-result-container">

        </div>
    </div>
</div>

        <div class="index-about-mobile">
            <i> Like what you do, do what you like </i>
        </div>
    </div>
    
    <div class="index-middle">
        <!-- Main Content -->
        


<div class="post-container">
    <div class="post-title">
        qmetatype-knows-your-types
    </div>

    <div class="post-meta">
        <span class="attr">发布于：<span>2021-05-29 10:05:15</span></span>
        
        <span class="attr">标签：/
        
        <a class="tag" href="/myblog/tags/#Qt" title="Qt">Qt</a>
        <span>/</span>
        
        <a class="tag" href="/myblog/tags/#文章翻译" title="文章翻译">文章翻译</a>
        <span>/</span>
        
        
        </span>
        <span class="attr">访问：<span id="busuanzi_value_page_pv"></span>
</span>
</span>
    </div>
    <div class="post-content no-indent">
        <blockquote>
<p>文章来源：<a target="_blank" rel="noopener" href="https://woboq.com/blog/qmetatype-knows-your-types.html">qmetatype-knows-your-types</a></p>
</blockquote>
<p>Q_DECLARE_METATYPE宏声明过后，只是生成了元信息，可以被QVariant识别，还不能用于队列方式的信号、槽，需要用qRegisterMetaType进行注册。而<strong>qRegisterMetaType要求”全定义”，也就是提供类的”复制构造函数”和”赋值操作符”</strong>。</p>
<p>前面那种简单类型，C++编译器默认提供浅拷贝的”复制构造函数”和”赋值操作符”实现，可以直接用。</p>
<p>而复杂一些的类，就要提供”全定义”。</p>
<p>(顺带一提，信号的参数可以是任意注册过的对象，而C++11的lambda、std::bind也是对象，只要注册过，也是可以通过信号参数发送出去的。)</p>
<p>QMetaType是Qt获得关于类型的运行时动态信息的方式。它支持自定义类型的QVariant包装、队列连接参数的复制等等。</p>
<p>如果你想知道<code>Q_DECLARE_META_TYPE</code>或<code>qRegisterMetaType</code>做什么，什么时候使用(或不使用)它们，请继续阅读。本文将描述关于QMetaType您需要了解的内容:它的目的是什么;如何使用它;以及它是如何工作的。</p>
<h1 id="Why-does-Qt-need-runtime-dynamic-type-information"><a href="#Why-does-Qt-need-runtime-dynamic-type-information" class="headerlink" title="Why does Qt need runtime dynamic type information?"></a>Why does Qt need runtime dynamic type information?</h1><p>让我们从一些历史开始。**QMetaType是Qt 4.0中引入的。创建它是为了有可能拥有异步信号和槽(Qt::QueuedConnection)**。为了使queued slots槽工作，我们必须复制参数并将它们存储在稍后处理的事件中。我们还需要在调用完插槽后删除这些副本。(注意:使用Qt::DirectConnection时不需要这个:直接在堆栈上使用指向参数的指针。)</p>
<p>QMetaObject::activate中的代码调度(dispatch)信号有一个指向参数void*的指针数组。(要了解更多信息，请阅读<a target="_blank" rel="noopener" href="https://woboq.com/blog/how-qt-signals-slots-work.html">信号和槽的工作方式</a>)。但是，当时Qt所知道的关于参数类型的所有信息都是它们作为字符串的名称，由moc提取。</p>
<p>QMetaType提供了一种从字符串(例如，“QPoint”)获取一个要复制或销毁的对象。Qt将使用<code>void *QMetaType::create(int type，void *copy)</code>和<code>QMetaType::destroy(int type，void *data)</code>来复制和销毁参数，其中int类型是使用<code>QMetaType::type(const char *typeName)</code>使用参数的类型名获得的，就像moc提供的那样。QMetaType还为开发人员提供了在元类型数据库中注册任何类型的方法。</p>
<p>QMetaType的另一个用例是QVariant。Qt 3.x的QVariant只支持内置Qt类型，因为包含的任意类型也需要与包装QVariant一起复制或销毁。但是<strong>在QMetaType的帮助下，QVariant能够包含任何已注册的类型，因为QVariant现在可以复制和销毁所包含的对象实例</strong>。</p>
<h1 id="What-information-does-QMetaType-keep"><a href="#What-information-does-QMetaType-keep" class="headerlink" title="What information does QMetaType keep?"></a>What information does QMetaType keep?</h1><p>自从Qt 4.0以来，发生了很多变化。我们现在有了QtScript和QML，它们大量使用动态类型集成。我们需要做很多优化。</p>
<p>下面是元类型系统中每个类型的信息列表:</p>
<ul>
<li>注册的类型名称。有一个名称索引用于快速查找元类型id。从Qt 4.7开始，甚至可以用不同的名称注册相同的类型(对typedef很有用)。</li>
<li>(复制)构造函数和析构函数(是否到位)。</li>
<li><strong>Size</strong> ，以了解为堆栈或内联成员结构分配多少空间。</li>
<li><strong>Flags</strong> ，指定与QTypeInfo相同的信息(见下文)或转换类型。</li>
<li>自定义转换函数，由QMetaType::registerConverter设置。</li>
<li>QMetaObject，如果存在，则包含与类型关联的元QObject数据。</li>
</ul>
<h2 id="QTypeInfo"><a href="#QTypeInfo" class="headerlink" title="QTypeInfo"></a><code>QTypeInfo</code></h2><p>QTypeInfo是一个与QMetaType正交的trait类，它允许开发人员手动指定(使用Q_DECLARE_TYPEINFO)一个类型是可移动的(使用memmove)，或者它的构造函数&#x2F;析构函数是否需要运行。这主要用于优化容器，如QVector。</p>
<p>例如，可以使用memmove移动隐式共享类。而正常的复制应该首先在复制构造函数中增加引用计数，然后在析构函数中减少引用计数。</p>
<p>c++ 11引入了move构造函数和标准类型特征来解决这个问题，但由于QTypeInfo是在c++ 11之前设计的，Qt仍然必须使用旧的编译器，所以我们不得不放弃。</p>
<h1 id="How-does-it-work"><a href="#How-does-it-work" class="headerlink" title="How does it work?"></a>How does it work?</h1><p>由于历史原因，内置类型和自定义类型之间有很大的区别。对于QtCore中的内置类型，每个元类型函数基本上都是一个开关，每个类型都有特殊的代码。在Qt 5.0中，这被重构以大量使用模板。(参见QMetaTypeSwitcher)。但在本文中，我们感兴趣的是它如何用于自定义注册类型。</p>
<p>这里有一个简单的QVector<QCustomTypeInfo>，它包含所有信息和一堆函数指针。</p>
<h2 id="The-Q-DECLARE-METATYPE-macro"><a href="#The-Q-DECLARE-METATYPE-macro" class="headerlink" title="The Q_DECLARE_METATYPE macro."></a>The <code>Q_DECLARE_METATYPE</code> macro.</h2><p>该宏专门针对特定类型的模板类QMetaTypeId。(事实上，它实际上特化了QMetaTypeId2类，大多数代码使用QMetaTypeId2。我不知道QMetaTypeId2背后的确切原因。也许这样Qt就可以添加更多的内置类型，而不会破坏之前使用Q_DECLARE_METATYPE的代码。)</p>
<p>QMetaTypeId用于在编译时确定类型的元类型id。</p>
<p>QMetaTypeId::qt_metatype_id是由qMetaType<T>()调用的函数。在第一次调用该函数时，它将调用QMetaType中的某个内部函数，使用宏中指定的名称为该类型注册和分配元类型id。然后将该id存储在一个静态变量中。</p>
<p>除了名称，编译器使用模板自动推断出所有其他信息。</p>
<h2 id="qRegisterMetaType"><a href="#qRegisterMetaType" class="headerlink" title="qRegisterMetaType"></a>qRegisterMetaType</h2><p>使用Q_DECLARE_METATYPE注册的类型将在第一次使用qMetaTypeId()时实际注册(并分配一个id)。例如，当一个类型被封装在QVariant中时就是这种情况。但是<strong>在连接信号和插槽时还没有注册。在这种情况下，您需要使用qRegisterMetaType强制第一次使用</strong>。</p>
<h1 id="Automatic-registration"><a href="#Automatic-registration" class="headerlink" title="Automatic registration."></a>Automatic registration.</h1><p>开发人员经常忘记注册他们的元类型，直到他们看到编译错误或运行时错误告诉他们这样做。但如果这不是必须的，那不是很好吗?必须使用Q_DECLARE_METATYPE的唯一原因是获取名称。但在有些情况下，我们可以在运行时知道这个名称，而不需要这个宏。例如，如果T已经注册，则QList<T>，我们可以查询元类型系统，并使用”QList&lt;” + QMetaType::name(qMetaTypeId<T>()) + “&gt;”构造名称。</p>
<p>我们对一堆模板化类这样做，例如:QList, QVector, QSharedPointer, QPointer, QMap, QHash，…<br>通过moc: T::staticMetaObject.className() + “*”提供的信息，我们还可以确定指向QObject子类的指针的名称。</p>
<p>从Qt 5.5开始，我们还将自动声明<strong>Q_GADGET</strong>和Q_ENUM。</p>
<p>这就是Q_DECLARE_METATYPE，但是您仍然需要调用qRegisterMetaType来在Q_PROPERTY中使用这些类型或作为信号&#x2F;插槽队列连接中的参数。从Qt 5开始。然而，moc生成的代码将为你调用qRegisterMetaType，如果moc可以确定该类型可以注册为元类型。</p>
<h1 id="Research"><a href="#Research" class="headerlink" title="Research"></a>Research</h1><p>在Qt 5.0之前，我试图调查在不需要Q_DECLARE_METATYPE名称的情况下是否不能去掉Q_DECLARE_METATYPE。它的工作原理是这样的:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt; QMetaTypeId &#123;</span><br><span class="line">    <span class="function"><span class="type">static</span> <span class="type">int</span> <span class="title">qt_metatype_id</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="type">static</span> <span class="type">int</span> typeId = QMetaType::<span class="built_in">registerMetaType</span>(<span class="comment">/*...*/</span>);</span><br><span class="line">        <span class="keyword">return</span> typeId;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>根据c++标准，每种类型都应该有一个变量QMetaTypeId::qt_metatype_id()::typeId实例。但在实践中，一些编译器或链接器不遵守这个规则。特别是，在Windows上，即使使用正确的导出宏，每个库也会有一个实例。因此，我们总是需要一个名称标识符，但我们没有。(我们不希望依赖RTTI)。因此，我们只在Qt 5中注册我们可以知道名称的类型。</p>
<hr>

        
            <div class="donate-container">
    <div class="donate-button">
        <button id="donate-button">赞赏</button>
    </div>
    <div class="donate-img-container hide" id="donate-img-container">
        <img id="donate-img" src="" data-src="/myblog/img/donate.jpg">
        <p> 感谢鼓励 </p>
    </div>
</div>
        
        <br />
        <div id="comment-container">
        </div>
        <div id="disqus_thread"></div>
        <div id="lv-container"></div>
        <div class="giscus"></div>
    </div>
</div>

    </div>
</div>


<footer class="footer">
    <ul class="list-inline text-center">
        
        

        

        

        
        <li>
            <a target="_blank"  href="https://github.com/samuel-24276">
                            <span class="fa-stack fa-lg">
                                <i class="iconfont icon-github"></i>
                            </span>
            </a>
        </li>
        

        

    </ul>
    
    <p>
        <span>/</span>
        
        <span><a target="_blank" rel="noopener" href="https://github.com/samuel-24276">samuel-24276&#39;s Page</a></span>
        <span>/</span>
        
        <span><a target="_blank" rel="noopener" href="https://beian.miit.gov.cn/">北京-未备案</a></span>
        <span>/</span>
        
        <span><a href="#">返回顶部</a></span>
        <span>/</span>
        
    </p>
    
    <p>
        <span id="busuanzi_container_site_pv">
            <span id="busuanzi_value_site_pv"></span>PV
        </span>
        <span id="busuanzi_container_site_uv">
            <span id="busuanzi_value_site_uv"></span>UV
        </span>
        Created By <a target="_blank" rel="noopener" href="https://hexo.io/">Hexo</a>  Theme <a target="_blank" rel="noopener" href="https://github.com/aircloud/hexo-theme-aircloud">AirCloud</a></p>
</footer>




</body>

<script>
    // We expose some of the variables needed by the front end
    window.hexo_search_path = "search.json"
    window.hexo_root = "/myblog/"
    window.isPost = true
</script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>

<script src="/myblog/js/index.js"></script>

<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>






    <script type="text/javascript">
       (function() {
           if (typeof LivereTower === 'function') { return; }

           var j, d = document.getElementById('lv-container');

           d.setAttribute('data-id','city');
           d.setAttribute('data-uid' , 'MTAyMC80OTA5OC8yNTU5Mw==');

           j = document.createElement('script');
           j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
           j.async = true;

           d.appendChild(j);
       })();
    </script>
    <noscript>为正常使用来必力评论功能请激活JavaScript</noscript>
    </div>

</html>
